%%% Copyright 2010 Google Inc.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%%     http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.

%%% @copyright 2010 Google Inc. All Rights Reserved
%%% @author <jacoby@google.com> Jacoby Thwaites [http://sites/madcloud]
%%% @version {@version}
%%% @doc
%%% TXM application and supervisor module. This module is the root
%%% supervisor in the supervision tree for TXM.
%%%
%%% Standard info, warning and error log functions are in this module.
%%%
%%% On startup, the `txm_log' module is added to the node error_logger.
%%%
%%% @headerfile "../include/txm.hrl"
-module(txm).

-compile(export_all).

%% See http://www.erlang.org/doc/man/application.html
-behaviour(application).

%% See http://www.erlang.org/doc/man/supervisor.html
-behaviour(supervisor).

%% Include record definitions, macros and constants.
-include("txm.hrl").
-include("web_srv.hrl").

%% Application behaviour exports
-export([start/2, stop/1]).

%% Supervisor behaviour exports
-export([start_link/0]).
-export([init/1]).

%%% ---------------------------------------------------------------------------
%%% Client functions
%%% ---------------------------------------------------------------------------

%% Module no-arg start delegates to application. Application environment vars
%% are used to determine startup type (local or replica) and optional services
%% such as web_srv module.
start() -> application:start(txm).

%% Module no-arg stop delegates to application
stop() -> application:stop(txm).

%% Module start_link delegates to supervisor.
%% @spec start_link() -> {ok, pid()}
start_link() ->
  ?OK_RETURN(Mode) = application:get_env(?TXM_APP, ?ENV_STARTUP),
  ?OK_RETURN(Optional) = application:get_env(?TXM_APP, ?ENV_STARTUP_OPTIONS),
  supervisor:start_link({local, ?MODULE}, ?MODULE, [Mode, Optional]).

%%% ---------------------------------------------------------------------------
%%% Application callback functions
%%% ---------------------------------------------------------------------------

%% @doc If we need to pass a state to the stop() function, then a third
%% term can be added to the return tuple.
%% @spec start(atom(), list()) -> {ok, pid()} | {ok, pid(), term()}
start(normal, _Args) ->
    start_link().

%% @doc Stops the application.
%% @spec stop(term()) -> ok
stop(_State) ->
    txm_log:stop(),
    ?OK_RETURN.

%%% ---------------------------------------------------------------------------
%%% Supervisor callback functions
%%% ---------------------------------------------------------------------------

%% @doc Returns restart specification and list of child specifications.
%% Where appropriate, implementation modules (eg prog for prog_srv) are
%% specified.
%% @spec init(list()) -> {ok, term()}
init([Mode, Options]) ->

  % Start timer process if not already.
  timer:start(),

  % Start our log handler.
  txm_log:start(),

  % Compute the child specifications.
  ChildSpecs = lists:append(
                 compulsory_child_specs(Mode),
                 optional_child_specs(Options)),

  ?INFO([init, {mode, Mode}, {options, Options}]),
  ?OK_RETURN({{one_for_all, 1, 1}, ChildSpecs}).

%% @doc Returns the list of child specifications required on all TXM nodes.
%% @spec compulsory_child_specs(local | replica) -> list()
compulsory_child_specs(Mode) ->

  [
    {node_srv,
     {node_srv, start_link, [Mode]},
     permanent,
     ?SHUTDOWN_TIME,
     worker,
     [node_srv]},

    % The program database server.
    {prog_srv,
     {prog_srv, start_link, [prog]},
     permanent,
     ?SHUTDOWN_TIME,
     worker,
     [prog_srv]},

    % The supervisor for all view_fsm processes started by clients.
    {view_sup,
     {view_sup, start_link, []},
     permanent,
     ?SHUTDOWN_TIME,
     supervisor,
     [view_sup]},

    % The view db server cleans dying view_fsm entries from the view database.
    {view_db,
     {view_db, start_link, []},
     permanent,
     ?SHUTDOWN_TIME,
     worker,
     [view_db]},

    % The process cache used by process and fieldset servers.
    {process_cache,
     {cache_srv, start_link, [process_cache]},
     permanent,
     ?SHUTDOWN_TIME,
     worker,
     [process_cache]}].

%% @doc Returns the list of child specifications obtained by appending the
%% computed child specification for each option in turn.
%% @spec optional_child_specs(list()) -> list()
optional_child_specs(Options) ->
  lists:foldl(fun(Option, Acc) ->
                   [child_spec(Option) | Acc]
              end, [], Options).

%% @doc Returns the child specification for the web_srv option.
%% @spec child_spec(term()) -> term()
child_spec({web_srv, Port}) ->
  {web_srv,
   {inets, start, web_srv_args(Port)},
   permanent,
   ?SHUTDOWN_TIME,
   worker,
   [web_srv]};

%% @doc Returns the child specification for the web_view option which
%% supports HTTP clients.
%% #2
child_spec({web_view, Port}) ->
  {web_view,
    {inets, start, web_view_args(Port)},
    permanent,
    ?SHUTDOWN_TIME,
    worker,
    [web_view]}.

%% @doc Returns inets:start() arguments required to start httpd.
%% See [http://www.erlang.org/doc/man/httpd.html]
%% @spec web_srv_args(integer()) -> list()
web_srv_args(Port) ->
  {ok, CallbackTimeout} =
    application:get_env(?TXM_APP, ?ENV_WEB_CALLBACK_TIMEOUT_SECS),
  [httpd, [
            {modules, [
                        mod_alias,
                        mod_auth,
                        mod_esi,
                        mod_actions,
                        mod_cgi,
                        mod_dir,
                        mod_get,
                        mod_head,
                        mod_log,
                        mod_disk_log
                        ]},
            {port, Port},
            {server_name, "txm_web_srv"},
            {server_root, "/tmp"},
            {document_root, "../priv"},
            {erl_script_alias, {"/txm", [web_srv]}},
            {erl_script_timeout, CallbackTimeout},
            {mime_types,[
                          {"html","text/html"},
                          {"css","text/css"},
                          {"js","application/x-javascript"},
                          {"xsl", "text/xml"}
                          ]}],
   stand_alone].

%% @doc Returns inets:start() arguments required to start httpd.
%% See [http://www.erlang.org/doc/man/httpd.html]
%% @spec web_view_args(integer()) -> list()
web_view_args(Port) ->
  {ok, CallbackTimeout} =
    application:get_env(?TXM_APP, ?ENV_WEB_CALLBACK_TIMEOUT_SECS),
  [httpd, [
            {modules, [
                        mod_alias,
                        mod_auth,
                        mod_esi,
                        mod_actions,
                        mod_cgi,
                        mod_dir,
                        mod_get,
                        mod_head,
                        mod_log,
                        mod_disk_log
                        ]},
            {port, Port},
            {server_name, "txm_web_view"},
            {server_root, "/tmp"},
            {document_root, "../priv"},
            {erl_script_alias, {"/view", [web_view]}},
            {erl_script_timeout, CallbackTimeout},
            {mime_types,[
                          {"html","text/html"},
                          {"css","text/css"},
                          {"js","application/x-javascript"},
                          {"xsl", "text/xml"}
                          ]}],
   stand_alone].
